package danebrown;

import com.google.common.base.Strings;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.tags.Tag;
import io.swagger.v3.parser.OpenAPIV3Parser;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class OpenAPIParser {
    public static void main(String[] args) {
        scan(null);

    }

    public static void scan(String csvFileName) {
        if (Strings.isNullOrEmpty(csvFileName)) {
            String timestamp = String.valueOf(System.currentTimeMillis());
            csvFileName = "swagger_api_info_" + timestamp + ".xlsx";
        }
        List<String> swaggerUrl = new ArrayList<>();
        swaggerUrl.add("http://10.8.38.152:8094/nc-user-ind/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8093/nc-user-org/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8091/nc-user-query/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8104/nc-user-sync/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8092/nc-user-opr/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8090/nc-user-staff-service/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8080/nc-channel-auth-outer/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8086/nc-channel-backend/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8082/nc-channel-msg/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8083/nc-channel-search/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8081/nc-channel-auth-inner-service/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8084/nc-channel-log/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8085/nc-channel-mix/v3/api-docs");
        swaggerUrl.add("http://10.8.38.153:8087/nc-operation-base/v3/api-docs");
        swaggerUrl.add("http://10.8.38.155:8095/nc-issuance-book-dcm/v3/api-docs");
        swaggerUrl.add("http://10.8.38.155:8097/nc-issuance-book-query/v3/api-docs");
        swaggerUrl.add("http://10.8.38.155:8102/nc-issuance-common/v3/api-docs");
        swaggerUrl.add("http://10.8.38.155:8103/nc-issuance-dist/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8380/nc-public-msg/v3/api-docs");
        swaggerUrl.add("http://10.8.38.152:8381/nc-public-wkcl/v3/api-docs");
//
//        swaggerUrl.add("http://dev.center.cfae.cn/nc-issuance-book-query/v3/api-docs");
//        swaggerUrl.add("http://dev.center.cfae.cn/nc-issuance-book-dcm/v3/api-docs");
        List<SwaggerApiInfo> infos = new ArrayList<>();
        for (String url : swaggerUrl) {
            Map<String, PathItem> pathItemMap = parse2csv(url);
            if (pathItemMap == null || pathItemMap.isEmpty()) {
                continue;
            }
            infos.addAll(collectSwaggerInfo(pathItemMap));
        }
        print2xlsx(infos, csvFileName);
    }

    private static void print2xlsx(List<SwaggerApiInfo> infos, String fileUrl) {

        List<List<String>> data = new ArrayList<>();

        // 添加表头
        List<String> header = Arrays.asList("标签", "接口名称", "路径", "METHOD", "接口摘要", "接口描述");
        data.add(header);

        // 添加数据行
        for (SwaggerApiInfo r : infos) {
            List<String> row = Arrays.asList(
                    r.getTags(),
                    r.getOperationId(),
                    r.getPath(),
                    r.getMethod(),
                    r.getSummary(),
                    r.getDescription()
            );
            data.add(row);
        }
        XlsxWriter.write(fileUrl, "SwaggerApiInfo", data);

        System.out.println("解析完成！共找到 " + infos.size() + " 个路径。");
    }

    private static List<SwaggerApiInfo> collectSwaggerInfo(Map<String, PathItem> paths) {

        System.out.println("=== API 接口详情 (CSV格式) ===");
        // 输出CSV表头
        System.out.println("标签,接口名称,路径,METHOD,接口摘要,接口描述");
        List<SwaggerApiInfo> infos = new ArrayList<>();
        // 遍历路径
        for (Map.Entry<String, PathItem> pathEntry : paths.entrySet()) {
            String path = pathEntry.getKey();
            PathItem pathItem = pathEntry.getValue();
            SwaggerApiInfo apiInfo = null;
            // 遍历操作（GET, POST, PUT, DELETE 等）
            // 检查每种HTTP方法
            if (pathItem.getGet() != null) {
                infos.add(printOperationDetails(path, "GET", pathItem.getGet()));
            }
            if (pathItem.getPost() != null) {
                infos.add(printOperationDetails(path, "POST", pathItem.getPost()));
            }
            if (pathItem.getPut() != null) {
                infos.add(printOperationDetails(path, "PUT", pathItem.getPut()));
            }
            if (pathItem.getDelete() != null) {
                infos.add(printOperationDetails(path, "DELETE", pathItem.getDelete()));
            }
            if (pathItem.getPatch() != null) {
                infos.add(printOperationDetails(path, "PATCH", pathItem.getPatch()));
            }
            if (pathItem.getHead() != null) {
                infos.add(printOperationDetails(path, "HEAD", pathItem.getHead()));
            }
            if (pathItem.getOptions() != null) {
                infos.add(printOperationDetails(path, "OPTIONS", pathItem.getOptions()));
            }
            if (pathItem.getTrace() != null) {
                infos.add(printOperationDetails(path, "TRACE", pathItem.getTrace()));
            }
        }
        return infos;

    }

    private static Map<String, PathItem> parse2csv(String swaggerUrl) {
        // Knife4j OpenAPI3 JSON 文件路径或 URL
        // 可以是本地文件路径，如: "file:///path/to/your/api-docs.json"
        // 或者是 Knife4j 的 API 文档地址，如: "http://localhost:8080/v3/api-docs"

        // 使用示例文件进行测试
//        String swaggerUrl = "file:///C:\\Users\\010400237\\Downloads\\default_OpenAPI_3.json";

        // 实际使用时，可以修改为你的 Knife4j API 文档地址：


        // 配置解析选项，适配 Knife4j 的扩展特性
        ParseOptions parseOptions = new ParseOptions();
        parseOptions.setResolve(true); // 解析 $ref 引用
        parseOptions.setResolveFully(true); // 完全解析所有引用
        parseOptions.setFlatten(true); // 扁平化处理

        // 使用 SwaggerParseResult 获取更详细的解析信息
        SwaggerParseResult parseResult = new OpenAPIV3Parser().readLocation(swaggerUrl, null, parseOptions);

        // 检查解析过程中的消息和警告
        if (parseResult.getMessages() != null && !parseResult.getMessages().isEmpty()) {
            System.out.println("解析消息:");
            for (String message : parseResult.getMessages()) {
                System.out.println("  - " + message);
            }
            System.out.println();
        }

        OpenAPI openAPI = parseResult.getOpenAPI();
        if (openAPI == null) {
            System.out.println("无法解析 OpenAPI 规范，请检查 URL 或文件路径。");
            System.out.println("可能的原因：");
            System.out.println("1. URL 不可访问或文件不存在");
            System.out.println("2. JSON 格式不正确");
            System.out.println("3. 不是有效的 OpenAPI 3.0 规范");
            return null;
        }

        // 输出基本信息
        Info info = openAPI.getInfo();
        if (info != null) {
            System.out.println("=== API 基本信息 ===");
            System.out.println("标题: " + info.getTitle());
            System.out.println("版本: " + info.getVersion());
            System.out.println("描述: " + info.getDescription());
            if (info.getContact() != null) {
                System.out.println("联系人: " + info.getContact().getName());
            }
            System.out.println();
        }

        // 输出标签信息（Knife4j 通常会使用标签来分组接口）
        List<Tag> tags = openAPI.getTags();
        if (tags != null && !tags.isEmpty()) {
            System.out.println("=== API 标签 ===");
            for (Tag tag : tags) {
                System.out.println("标签: " + tag.getName() + " - " + tag.getDescription());
            }
            System.out.println();
        }

        // 提取路径和操作
        Map<String, PathItem> paths = openAPI.getPaths();
        if (paths == null || paths.isEmpty()) {
            System.out.println("未找到路径信息。");
            return null;
        }
        return paths;

    }

    /**
     * 输出单个操作的详细信息
     */
    private static SwaggerApiInfo printOperationDetails(String path, String method, Operation operation) {
        // 获取标签信息
        String tags = "";
        if (operation.getTags() != null && !operation.getTags().isEmpty()) {
            tags = String.join(", ", operation.getTags());
        }

        // 获取接口名称
        String operationId = operation.getOperationId() != null ? operation.getOperationId() : "";

        // 获取接口摘要
        String summary = operation.getSummary() != null ? operation.getSummary() : "";

        // 获取接口描述
        String description = operation.getDescription() != null ? operation.getDescription() : "";

        // 处理CSV中的特殊字符（双引号和换行符）
        tags = escapeCsvField(tags);
        operationId = escapeCsvField(operationId);
        path = escapeCsvField(path);
        method = escapeCsvField(method);
        summary = escapeCsvField(summary);
        description = escapeCsvField(description);
        SwaggerApiInfo apiInfo = new SwaggerApiInfo();
        apiInfo.setTags(tags);
        apiInfo.setOperationId(operationId);
        apiInfo.setPath(path);
        apiInfo.setMethod(method);
        apiInfo.setSummary(summary);
        apiInfo.setDescription(description);
        return apiInfo;

    }

    // 处理CSV字段中的特殊字符
    private static String escapeCsvField(String field) {
        if (field == null) {
            return "";
        }

        // 如果字段包含逗号、双引号或换行符，需要用双引号包围，并转义内部的双引号
        if (field.contains(",") || field.contains("\"") || field.contains("\n") || field.contains("\r")) {
            // 将双引号转义为两个双引号
            field = field.replace("\"", "\"\"");
            // 用双引号包围整个字段
            field = "\"" + field + "\"";
        }

        return field;
    }

    public static class SwaggerApiInfo {
        private String tags;
        private String operationId;
        private String path;
        private String method;
        private String summary;
        private String description;

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getSummary() {
            return summary;
        }

        public void setSummary(String summary) {
            this.summary = summary;
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public String getOperationId() {
            return operationId;
        }

        public void setOperationId(String operationId) {
            this.operationId = operationId;
        }

        public String getTags() {
            return tags;
        }

        public void setTags(String tags) {
            this.tags = tags;
        }
    }
}